MyBatis中ResultMap使用实例解析 您所在的位置:网站首页 mybatis 中 example 的使用 MyBatis中ResultMap使用实例解析

MyBatis中ResultMap使用实例解析

2024-01-31 19:46| 来源: 网络整理| 查看: 265

实例说明

该项目是在:Spring Boot整合MyBatis框架操作MySQL数据库实例的基础上继续深入使用MaBatis的各项功能。项目开源地址:https://github.com/Yitian-Zhang/springboot-learning(包含下述所有代码)。

实例说明:在通过MyBatis官方文档(地址:https://mybatis.org/mybatis-3/zh/index.html)学习MyBatis的使用时,发现在对ResultMap的使用,仅存在示例的配置说明,并没有一个具体的实例可供学习者操作,这使得学习过程中只停留在理论而不好实践。所以本文依据MyBatis官方文档中给出的最复杂的一个ResultMap映射结果集和相应SELECT查询语句,反推出其中的表结构和类关系,从而还原该对该示例代码进行还原,使之成为可以作为实例操作的学习实例,并提供了开源。

如下是官方文档中,该示例配置代码的截图:

这里就以ResultMap中最复杂的一个示例入手来解析,ResultMap中的各项设置的使用,包括:Constructor,id,result,association,collection。此外,该项目中暂时没有使用discriminator的使用,后续将采用其他的实例进行说明。

创建实例表结构和类

如下表结构和类的关系是通过上述SQL SELECT查询语句和ResultMap结构进行反推得来的,用以模拟上述的示例。使用如下的SQL脚本创建相应的表结构:

SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for tb_author -- ---------------------------- DROP TABLE IF EXISTS `tb_author`; CREATE TABLE `tb_author` ( `author_id` int(11) NOT NULL AUTO_INCREMENT, `author_username` varchar(255) DEFAULT NULL, `author_password` varchar(255) DEFAULT NULL, `author_email` varchar(255) DEFAULT NULL, `author_bio` varchar(255) DEFAULT NULL, `author_favourite_section` varchar(255) DEFAULT NULL, PRIMARY KEY (`author_id`) ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; -- ---------------------------- -- Records of tb_author -- ---------------------------- BEGIN; INSERT INTO `tb_author` VALUES (2, 'yitian', '123', '[email protected]', 'my_bio', '12'); COMMIT; -- ---------------------------- -- Table structure for tb_blog -- ---------------------------- DROP TABLE IF EXISTS `tb_blog`; CREATE TABLE `tb_blog` ( `blog_id` int(11) NOT NULL, `blog_title` varchar(255) DEFAULT NULL, `author_id` int(11) DEFAULT NULL, PRIMARY KEY (`blog_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- ---------------------------- -- Records of tb_blog -- ---------------------------- BEGIN; INSERT INTO `tb_blog` VALUES (1, 'yitian_blog', 2); COMMIT; -- ---------------------------- -- Table structure for tb_comment -- ---------------------------- DROP TABLE IF EXISTS `tb_comment`; CREATE TABLE `tb_comment` ( `comment_id` int(11) NOT NULL, `comment_content` varchar(255) DEFAULT NULL, `post_id` int(11) DEFAULT NULL, PRIMARY KEY (`comment_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- ---------------------------- -- Records of tb_comment -- ---------------------------- BEGIN; INSERT INTO `tb_comment` VALUES (1, 'Comment1', 1); INSERT INTO `tb_comment` VALUES (2, 'Comment2', 1); INSERT INTO `tb_comment` VALUES (3, 'Comment3', 2); INSERT INTO `tb_comment` VALUES (4, 'Comment4', 3); COMMIT; -- ---------------------------- -- Table structure for tb_post -- ---------------------------- DROP TABLE IF EXISTS `tb_post`; CREATE TABLE `tb_post` ( `post_id` int(11) NOT NULL, `post_subject` varchar(255) DEFAULT NULL, `author_id` int(11) DEFAULT NULL, `draft_status` int(11) DEFAULT NULL, `post_content` varchar(255) DEFAULT NULL, `blog_id` int(11) DEFAULT NULL, PRIMARY KEY (`post_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- ---------------------------- -- Records of tb_post -- ---------------------------- BEGIN; INSERT INTO `tb_post` VALUES (1, 'subject1', 2, 1, 'Post1', 1); INSERT INTO `tb_post` VALUES (2, 'subject2', 2, 1, 'Post2', 1); INSERT INTO `tb_post` VALUES (3, 'subject3', 2, 1, 'Post3', 1); COMMIT; -- ---------------------------- -- Table structure for tb_tag -- ---------------------------- DROP TABLE IF EXISTS `tb_tag`; CREATE TABLE `tb_tag` ( `tag_id` int(11) NOT NULL, `tag_content` varchar(255) DEFAULT NULL, `post_id` int(11) DEFAULT NULL, PRIMARY KEY (`tag_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- ---------------------------- -- Records of tb_tag -- ---------------------------- BEGIN; INSERT INTO `tb_tag` VALUES (1, 'Tag1', 1); INSERT INTO `tb_tag` VALUES (2, 'Tag2', 2); INSERT INTO `tb_tag` VALUES (3, 'Tag3', 3); COMMIT; SET FOREIGN_KEY_CHECKS = 1;

上述SQL脚本创建了如下的表结构,并插入了一些示例的数据:

tb_author:Blog作者表,与Blog一对一的关系。tb_blog:Blog表,与author一对一,与post一对多。tb_post:Post文章表,与blog多对一,与comment一对多,与tag一对多。tb_comment:文章评论表,与post多对一。tb_tag:文章标签表,与post多对一。

相应的创建上述5个表所对应的Java类结构如下:(其中Data注解为使用的Lombok)

@Data public class Blog { private Integer id; private String title; private Author author; private List posts; } @Data public class Author { private Integer id; private String userName; private String password; private String email; private String bio; private String favouriteSection; } @Data public class Post { private Integer id; private String subject; private Author author; private List comments; private List tags; private Integer draftStatus; private String content; } @Data public class Comment { private Integer id; private String content; } @Data public class Tag { private Integer id; private String content; }

所以根据以上的关系可以看出:一个blog包含一个author和多个post,一个post包含一个author(实际上和前一个author相同),多个comment和多个tag,comment和tag相互独立。

在MyBatis映射文件中使用ResultMap 单表ResultMap简单使用

有了以上的表和类,并插入了相应的简单数据用于查询,下面就可以使用resultMap来编写对应的映射结果集了。首先使用Author来实现一个单表的查询操作(暂时没有使用多表关联),authorMapper.xml文件内容如下所示:

SELECT * FROM tb_author WHERE author_id=#{id}

如上所示,仅是简单使用id对author的记录进行查询,并将查询结果使用resultMap进行映射。其中需要关注的点如下:

select元素和resultMap元素使用resultMap中的id属性与select元素中的resultMap属性进行关联。select中若使用resultMap则resultType属性则会失效。resultMap元素中,type属性指明返回集的类型。和元素都是表明表中的列名和类对象之间的映射关系,id元素用于表中主键(文档中说如此设置有利于提高查询性能,但没有说明原因,为提高性能需要进一步探索),result元素用于普通列名和属性名的对应,property指明类中属性,column指明对应的查询表列名,如果查询中对查询列名设置了as 别名,则column应是对应的别名。

说明:myBatis中实际上有强大的自动映射功能,可以使用别名自动映射,或者使用自动驼峰式映射,但这里为了学习resultMap的使用,没有使用别名的方式进行。

使用如上的映射文件时,创建如下对应的AuthorMapper接口和getAuthor方法,该接口无需实现,既可以使用:

@Repository public interface AuthorMapper { Author getAuthor(Integer id); }

编写简单的Controller用于上述方法的测试,得到的结果如下:

上述方式为仅使用配置文件的方式,此外还可以使用anotation或provider的方式实现同样的功能:(注:此时需要将authorMappe.xml文件中的select元素注释掉)

@Repository public interface AuthorMapper { /** * 简单的SQL查询,可以不需要Provider和mapper.xml文件中的元素 * 但由于Author的属性值和表的列名不对应,所以需要mapper.xml文件中定义ResultMap进行映射 */ @Select("SELECT * FROM tb_author WHERE author_id=#{id}") @ResultMap("authorMap") Author getAuthor(Integer id); }

或者创建AuthorProvider类定义相关的SQL语句,并配合ResultMap进行使用:

// 创建AuthorProvider.java文件 public class AuthorProvider { public String getAuthor(Integer id) { return new SQL() { { SELECT("*"); FROM("tb_author"); WHERE("author_id=#{id}"); } }.toString(); } } // AuthorMapper.java文件 @SelectProvider(type = AuthorProvider.class, method = "getAuthor") @ResultMap("authorMap") Author getAuthorProvider(Integer id);

对于以上三种方式总结如下:

【只使用配置文件的方式】:可以使用select元素,指定对应的返回ResultMap的id,然后在AuthorMap接口中声明对应的getAuthor方法,来进行映射。 【使用注解配合使用配置文件的方式】:在不使用如下的Select元素的情况下可以使用如下方式进行:

直接在接口方法中使用@Select注解,针对较简单的SQL语句较为适合使用@SelectProvider并创建相应的Provider类,实现sql语句,针对复杂的SQL场景比较适用。 

同时以上两种注解方式中,都可以使用@ResultMap注解,指明authorMapper.xml配置文件中用于设置查询结果的ResultMap。

简单的ResultMap的使用在此就不再详述了,参考mybatis的官方文档可以进行很好的学习。下面来对最上面说明的示例映射进行解析。

复杂的ResultMap映射关系解析

现在回到上述最复杂的那个列子,需要创建并编写blogMapper.xml配置文件,其中select元素中的查询语句如下:

select B.blog_id, B.blog_title, A.author_id, A.author_username, A.author_password, A.author_email, A.author_bio, A.author_favourite_section, P.post_id, P.post_subject, P.author_id as post_author_id, P.draft_status as post_draft_status, P.post_content, C.comment_id, C.comment_content, T.tag_id, T.tag_content from tb_blog B left outer join tb_author A on B.author_id=A.author_id left outer join tb_post P on B.blog_id=P.blog_id left outer join tb_comment C on C.post_id=P.post_id left outer join tb_tag T on T.post_id=P.post_id where B.blog_id=#{id}

可以看到该SQL语句中,存在如下的注意事项:

select语句中,对于没有使用as别名的查询column,MyBatis会直接使用原有的列名进行映射。对于使用as别名的列,则使用别名进行映射。如果没有使用别名,并且resultmap没有设置映射关系时,mybatis会自动映射到相应的属性,如果无法映射则会报错。

创建该查询语句的ResultMap结构如下:

根据下图进行分析:

注意:该图为MyBatis示例文档中的解析图,与上述实际的代码有一定的出入。具体实现以代码为准,这里只是分析其映射关系。此外,在代码中暂时省去,将draft属性使用进行映射。

这两个元素都是设置将查询结果,根据column映射到property的对应关系的,是映射配置的最基本的内容。这两者之间的唯一不同是,id元素表示的结果是对象的标识属性,这会在比较对象实例时用到,这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射时(官方文档给出的说明)。

这两个元素可以使用的属性如下:

允许在将查询结果映射为类对象时,使用类相应的构造方法来设置属性的值。这里就是在得到查询结果后,使用如下的构造方法(参数使用了id与查询列进行对应),来设置了Blog类中id属性的值:

public Blog(Integer id) { this.id = id; }

注:如果无此构造方法,mybatis在映射结果集时将报错。

association关系表示“有一个”的关系,对应于上述的实例,就是blog中有一个author。一般使用嵌套结果映射的方式实现关联关系的映射:

在resultmap中的第一个association即表示:将查询结果的author_id, _username, _password, _email, _bio, _author_favourite_section列分别映射到对应的属性中。同时这些属性属于Author类,因此根据这些属性,创建一个Author类的实例对象,并将其映射到Blog中关联的author属性中。

...

表示“有多个”的关系。对应与如上的配置,即为:一个Blog有多个Post,反应到类结构上就是:

public class Blog { ... private List posts; }

只不过Post中还有另外的嵌套结果映射,包括一个Author author,对应于:

多个评论List comments,和多个标签List tags对应于:

结果测试

编写完成上述的ResultMap后,创建BlogMapper接口,并声明其中的getBlog方法如下:

@Repository public interface BlogMapper { Blog getBlog(Integer id); }

然后通过Controller进行测试,得到最后的结果为:

可以看到已经将SELECT语句得到的查询列,根据ResultMap映射为一个完整的Blog对象。以上方式为使用纯配置文件的方式进行的,也可以使用@SelectProvider注解来实现相应的方法,这里就不再给出。

实例总结

以上实例基于MyBatis官网文档中的例子,反推得到表和类的结构和关联关系,从而实现了简单的ResultMap和复杂ResultMap的使用,以加深对MyBatis的使用。

在对ResultMap的理解过程中,需要明确ResultMap定义的是对SQL查询结果集的映射结构。也就是官方文档中所说的,对于简单的语句根本不需要显式配置resultMap,因为mybatis为自定创建resultmap并完成映射,而对于复杂的返回结果集,resultmap描述的是结果集中所包含对象的关系。这是非常重要的。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有